/*
 *  Copyright 2012 yura.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.db.obj.vis.domain.wrappers;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.opu.db.obj.vis.domain.domains.AbstractAttributeEntry;
import org.opu.db.obj.vis.domain.domains.AttributeConnectionType;
import org.opu.db.obj.vis.domain.domains.AttributeId;
import org.opu.db.obj.vis.domain.domains.ObjectAttribute;
import org.opu.db.obj.vis.domain.domains.ObjectEntry;
import org.opu.db.obj.vis.domain.domains.ObjectId;
import org.opu.db.obj.vis.domain.domains.ObjectPriority;
import org.opu.db.obj.vis.domain.domains.SimpleAtribute;
import org.opu.db.obj.vis.domain.wrappers.ObjectWrapper;
import org.opu.db_vdumper.domain.DColumn;
import org.opu.db_vdumper.domain.DColumnRelation;
import org.opu.db_vdumper.domain.DTable;
import org.opu.db_vdumper.services.DomainService;

/**
 *
 * @author yura
 */
public class MappingHelper {

    public List<ObjectWrapper> createBaseMapping(DomainService domainService)
            throws SQLException{
        ArrayList<ObjectEntry> list = Lists.newArrayList();
        Map<String, DTable> tables = domainService.getTables();
        Map<String, AttributeId> attributeIds = Maps.newHashMap();
        Map<String, ObjectId> objectIds = Maps.newHashMap();

        for (DTable dTable : tables.values()) {
            ObjectId objectId = new ObjectId(dTable.getSchema(), dTable.getName());
            objectIds.put(generateTableId(dTable), objectId);

            ObjectEntry objectEntry = new ObjectEntry(objectId);
            objectEntry.setDescription("Table {" + dTable + '}');
            objectEntry.setPrioritys(Lists.<ObjectPriority>newArrayList());

            for (DColumn dColumn : dTable.getColumns()) {
                AttributeId attributeId = new AttributeId(dColumn.getName());
                attributeIds.put(generateColumnId(dTable, dColumn), attributeId);

                objectEntry.addAttribute(new SimpleAtribute(attributeId));
            }

            list.add(objectEntry);
        }

        List<ObjectWrapper> wrappers = Lists.newArrayList();

        Map<String, DColumn> columns = Maps.newHashMap();
        Map<DColumn, List<DColumn>> refferenceFrom = Maps.newHashMap();
        Map<DColumn, List<DColumn>> refferenceTo = Maps.newHashMap();

        obtainReferences(tables, columns, refferenceFrom, refferenceTo);

        for (ObjectEntry objectEntry : list) {
            ObjectId objectId = objectEntry.getId();
            ObjectWrapper objectWrapper = new ObjectWrapper(objectEntry);
            ArrayList<AbstractAttributeEntry> etalon = objectEntry.getAttributes();
            ArrayList<AbstractAttributeEntry> attributes = Lists.newArrayList();

            for (Iterator<AbstractAttributeEntry> it = etalon.iterator(); it.hasNext();) {
                AbstractAttributeEntry attribute = it.next();
                AttributeId id = attribute.getId();

                DColumn column = columns.get(objectId.getSchemaName() + "."
                        + objectId.getTableName() + "." + id.getName());

                List<DColumn> refferenceToCols = refferenceTo.get(column);
                List<DColumn> refferenceFromCols = refferenceFrom.get(column);

                if (refferenceToCols != null && !refferenceToCols.isEmpty()) {
                    it.remove();

                    for (DColumn dColumn : refferenceToCols) {
                        addAttr(objectIds, dColumn, id, attributes,
                                AttributeConnectionType.REFERENCE_FROM_OBJECT);
                    }
                } else if (refferenceFromCols != null && !refferenceFromCols.isEmpty()) {
                    it.remove();

                    if (refferenceFromCols.size() > 1) {
                        throw new RuntimeException("Column " + objectId + "."
                                + column + " has more then 1 refference "
                                + refferenceFromCols);
                    }

                    DColumn dColumn = refferenceFromCols.get(0);

                    addAttr(objectIds, dColumn, id, attributes,
                            AttributeConnectionType.REFERENCE_TO_OBJECT);
                }
            }

            attributes.addAll(etalon);
            objectEntry.setAttributes(attributes);


            objectEntry.commit();

            wrappers.add(objectWrapper);
        }

        return wrappers;
    }

    private void addAttr(Map<String, ObjectId> objectIds, DColumn dColumn,
            AttributeId id, ArrayList<AbstractAttributeEntry> attributes,
            AttributeConnectionType type) {
        ObjectId get = objectIds.get(generateTableId(dColumn.getTable()));
        ObjectAttribute objAttr = new ObjectAttribute();
        objAttr.setId(id);
        objAttr.setRefferencedObjectId(get);
        objAttr.setRefferencedAttributeName(dColumn.getName());
        objAttr.setConnectionType(type);
        attributes.add(objAttr);
//                        DColumn get1 = refferenceFromCols.get(0);
//                        objectWrapper.addAttribute(new AttributeWrapper(objAttr, get1, attributes));
    }

    private void obtainReferences(Map<String, DTable> tables,
            Map<String, DColumn> columns,
            Map<DColumn, List<DColumn>> refferenceFrom,
            Map<DColumn, List<DColumn>> refferenceTo) {

        for (DTable dTable : tables.values()) {
            for (DColumn dColumn : dTable.getColumns()) {
                columns.put(generateColumnId(dTable, dColumn), dColumn);
            }
        }

        for (DTable dTable : tables.values()) {
            for (DColumnRelation relation : dTable.getRelations()) {
                List<DColumn> get = refferenceFrom.get(relation.getFrom());
                if (get == null) {
                    get = Lists.newArrayList();
                    refferenceFrom.put(relation.getFrom(), get);
                }

                DColumn col = columns.get(relation.getToTblSchem() + "."
                        + relation.getToTblName() + "."
                        + relation.getToColumnName());

                if (col == null) {
                    throw new RuntimeException();
                }

                get.add(col);

                List<DColumn> get2 = refferenceTo.get(col);
                if (get2 == null) {
                    get2 = Lists.newArrayList();
                    refferenceTo.put(col, get2);
                }

                get2.add(relation.getFrom());
            }
        }

    }

    private String generateTableId(DTable dTable) {
        return dTable.getSchema() + "." + dTable.getName();
    }

    private String generateColumnId(DTable dTable, DColumn dColumn) {
        return generateTableId(dTable) + "." + dColumn.getName();
    }

}
